package host.abetsy.recast.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import host.abetsy.recast.config.RecastProperties;
import host.abetsy.recast.domain.QuarkFile;
import host.abetsy.recast.service.QuarkOperationService;
import host.abetsy.recast.tools.FilePathTool;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.nio.charset.Charset;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class QuarkOperationServiceImpl implements QuarkOperationService {

    private final static Integer TIME_OUT = 10;

    private final static Integer PAGE_SIZE = 50;

    private final static List<String> FILE_TYPE = Arrays.asList("mp4", "mkv");

    private final static String LOGIN_URL = "https://pan.quark.cn/account/user_login_method_info?fr=pc&platform=pc";

    private final static String FILE_LIST_URL = "https://drive-pc.quark.cn/1/clouddrive/file/sort?pr=ucpro&fr=pc&_size=" + PAGE_SIZE + "&_fetch_total=1&pdir_fid=";

    private final static String CREATE_DIR = "https://drive-pc.quark.cn/1/clouddrive/file?pr=ucpro&fr=pc";

    private final static String COPY_FILE = "https://drive-pc.quark.cn/1/clouddrive/file/copy?pr=ucpro&fr=pc&sys=darwin&ve=3.23.2";

    private final static String TASK_STATUS = "https://drive-pc.quark.cn/1/clouddrive/task?pr=ucpro&fr=pc&task_id=";

    private final static String RENAME_FILE = "https://drive-pc.quark.cn/1/clouddrive/file/rename?pr=ucpro&fr=pc&uc_param_str=";

    @Resource
    private RecastProperties recastProperties;

    @Override
    public String load() {
        verification(recastProperties);
        FileUtil.del(recastProperties.getLocalPath());
        FileUtil.mkdir(recastProperties.getLocalPath());
        buildLocalCache(recastProperties.getRemotePathId(), recastProperties.getLocalPath());
        return "OK";
    }

    @Override
    public String update() {
        verification(recastProperties);
        Map<File, String> pathFidmap = new HashMap<>();
        pathFidmap.put(new File(recastProperties.getLocalPath()), recastProperties.getRemoteTmpPathId());
        List<File> dirList = FilePathTool.getAllSubDirectories(recastProperties.getLocalPath());
        for (File file : dirList) {
            String pdir_fid = pathFidmap.get(file.getParentFile());
            JSONObject jsonObject = new JSONObject();
            jsonObject.set("file_name", file.getName());
            jsonObject.set("dir_init_lock", false);
            jsonObject.set("dir_path", "");
            jsonObject.set("pdir_fid", pdir_fid);
            try (HttpResponse response = HttpRequest.post(CREATE_DIR)
                    .header("Cookie", recastProperties.getCookie())
                    .body(JSONUtil.toJsonStr(jsonObject))
                    .timeout(TIME_OUT * 1000).execute()) {
                if (response.isOk()) {
                    JSONObject entries = JSONUtil.parseObj(response.body());
                    pathFidmap.put(file, entries.getJSONObject("data").getStr("fid"));
                }
            }
        }
        List<File> fileList = FileUtil.loopFiles(recastProperties.getLocalPath(), x -> FileUtil.extName(x.getName()).equals("strm"));
        List<QuarkFile> quarkFileList = new ArrayList<>();
        for (File file : fileList) {
            String fileStr = FileUtil.readString(file, Charset.defaultCharset());
            QuarkFile quarkFile = JSONUtil.toBean(fileStr, QuarkFile.class);
            quarkFile.setNewParentFid(pathFidmap.get(file.getParentFile()));
            quarkFile.setNewName(FileUtil.mainName(file));
            quarkFileList.add(quarkFile);
        }
        Map<String, List<QuarkFile>> collect = quarkFileList.stream().collect(Collectors.groupingBy(QuarkFile::getNewParentFid));
        for (Map.Entry<String, List<QuarkFile>> entry : collect.entrySet()) {
            String keyParentFid = entry.getKey();
            List<String> subFidList = entry.getValue().stream().map(QuarkFile::getProtoFid).distinct().collect(Collectors.toList());
            JSONObject jsonObject = new JSONObject();
            jsonObject.set("to_pdir_fid", keyParentFid);
            jsonObject.set("filelist", subFidList);
            jsonObject.set("action_type", 2);
            jsonObject.set("copy_source", 1);
            try (HttpResponse response = HttpRequest.post(COPY_FILE)
                    .header("Cookie", recastProperties.getCookie())
                    .body(JSONUtil.toJsonStr(jsonObject))
                    .timeout(TIME_OUT * 1000).execute()) {
                if (response.isOk()) {
                    String taskId = JSONUtil.parseObj(response.body()).getJSONObject("data").getStr("task_id");
                    boolean flag = true;
                    while (flag) {
                        try (HttpResponse responseStatus = HttpRequest.get(TASK_STATUS + taskId)
                                .header("Cookie", recastProperties.getCookie())
                                .timeout(TIME_OUT * 1000).execute()) {
                            if (responseStatus.isOk()) {
                                int taskStatus = JSONUtil.parseObj(responseStatus.body()).getJSONObject("data").getInt("status");
                                if (taskStatus == 2) {
                                    flag = false;
                                }
                            }
                        }
                    }
                }
            }
            Map<String, QuarkFile> fullNameMap = entry.getValue().stream().collect(Collectors.toMap(QuarkFile::getProtoFullName, Function.identity()));
            JSONArray jsonArray = new JSONArray();
            while (jsonArray.size() != fullNameMap.size()) {
                jsonArray.clear();
                loadFileByParentFid(keyParentFid, jsonArray);
            }
            for (Object o : jsonArray) {
                JSONObject entries = JSONUtil.parseObj(o);
                if (entries.getInt("file_type") == 1) {
                    String fileName = entries.getStr("file_name");
                    if (fullNameMap.containsKey(fileName)) {
                        fullNameMap.get(fileName).setNewFid(entries.getStr("fid"));
                    }
                }
            }
        }
        List<QuarkFile> finalList = collect.values().stream().flatMap(List::stream).collect(Collectors.toList());
        for (QuarkFile quarkFile : finalList) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.set("fid", quarkFile.getNewFid());
            jsonObject.set("file_name", quarkFile.getNewName() + "." + quarkFile.getExtName());
            try (HttpResponse responseReName = HttpRequest.post(RENAME_FILE)
                    .header("Cookie", recastProperties.getCookie())
                    .body(JSONUtil.toJsonStr(jsonObject))
                    .timeout(TIME_OUT * 1000).execute()) {
                if (responseReName.isOk()) {
                    int status = JSONUtil.parseObj(responseReName.body()).getInt("status");
                }
            }
        }
        FileUtil.del(recastProperties.getLocalPath());
        FileUtil.mkdir(recastProperties.getLocalPath());
        return "OK";
    }

    private void loadFileByParentFid(String keyParentFid, JSONArray jsonArray) {
        try (HttpResponse response = HttpRequest.get(FILE_LIST_URL + keyParentFid).header("Cookie", recastProperties.getCookie()).timeout(TIME_OUT * 1000).execute()) {
            if (response.isOk()) {
                JSONObject entriesAll = JSONUtil.parseObj(response.body());
                jsonArray.addAll(entriesAll.getJSONObject("data").getJSONArray("list"));
                int tInt = entriesAll.getJSONObject("metadata").getInt("_total");
                if (tInt > PAGE_SIZE) {
                    int totalPages = PageUtil.totalPage(tInt, PAGE_SIZE) + 1;
                    for (int page = 2; page < totalPages; page++) {
                        try (HttpResponse responseSub = HttpRequest.get(FILE_LIST_URL + keyParentFid + "&_page=" + page).header("Cookie", recastProperties.getCookie()).timeout(TIME_OUT * 1000).execute()) {
                            if (responseSub.isOk()) {
                                jsonArray.addAll(JSONUtil.parseObj(responseSub.body()).getJSONObject("data").getJSONArray("list"));
                            }
                        }
                    }
                }
            }
        }
    }

    private void buildLocalCache(String fid, String localPath) {
        try (HttpResponse response = HttpRequest.get(FILE_LIST_URL + fid).header("Cookie", recastProperties.getCookie()).timeout(TIME_OUT * 1000).execute()) {
            if (response.isOk()) {
                JSONObject entriesAll = JSONUtil.parseObj(response.body());
                JSONArray jsonArray = entriesAll.getJSONObject("data").getJSONArray("list");
                int tInt = entriesAll.getJSONObject("metadata").getInt("_total");
                if (tInt > PAGE_SIZE) {
                    int totalPages = PageUtil.totalPage(tInt, PAGE_SIZE) + 1;
                    for (int page = 2; page < totalPages; page++) {
                        try (HttpResponse responseSub = HttpRequest.get(FILE_LIST_URL + fid + "&_page=" + page).header("Cookie", recastProperties.getCookie()).timeout(TIME_OUT * 1000).execute()) {
                            if (responseSub.isOk()) {
                                jsonArray.addAll(JSONUtil.parseObj(responseSub.body()).getJSONObject("data").getJSONArray("list"));
                            }
                        }
                    }
                }
                for (Object o : jsonArray) {
                    JSONObject entries = JSONUtil.parseObj(o);
                    if (entries.getInt("file_type") == 1) {
                        String fileName = entries.getStr("file_name");
                        String extNameLower = FileUtil.extName(fileName).toLowerCase();
                        if (FILE_TYPE.contains(extNameLower)) {
                            String mainName = FileUtil.mainName(fileName);
                            QuarkFile quarkFile = new QuarkFile();
                            quarkFile.setProtoFullName(fileName);
                            quarkFile.setProtoFid(entries.getStr("fid"));
                            quarkFile.setProtoName(mainName);
                            quarkFile.setExtName(extNameLower);
                            FileUtil.writeUtf8String(JSONUtil.toJsonStr(quarkFile), localPath + "/" + FileUtil.mainName(entries.getStr("file_name")) + ".strm");
                        }
                    } else {
                        buildLocalCache(entries.getStr("fid"), localPath + "/" + entries.getStr("file_name"));
                    }
                }
            } else {
                throw new RuntimeException("夸克网盘远程目录ID[RemotePathId]无效请重新设置");
            }
        } catch (Exception e) {
            throw new RuntimeException("夸克网盘远程目录ID[RemotePathId]无效请重新设置");
        }
    }

    private void verification(RecastProperties recastProperties) {
        if (StrUtil.isEmpty(recastProperties.getLocalPath())) {
            throw new RuntimeException("本地临时目录路径不能为空");
        } else {
            if (recastProperties.getLocalPath().endsWith("/")) {
                throw new RuntimeException("本地临时目录路径不能已/结尾");
            }
            boolean exist = FileUtil.exist(recastProperties.getLocalPath());
            if (!exist) {
                throw new RuntimeException("本地临时目录路径不存在: " + recastProperties.getLocalPath());
            }
        }
        if (StrUtil.isEmpty(recastProperties.getCookie())) {
            throw new RuntimeException("夸克网盘账号Cookie不能为空");
        } else {
            try (HttpResponse response = HttpRequest.get(LOGIN_URL).header("Cookie", recastProperties.getCookie()).timeout(TIME_OUT * 1000).execute()) {
                if (response.isOk()) {
                    JSONObject entries = JSONUtil.parseObj(response.body());
                    if (!entries.getBool("success")) {
                        throw new RuntimeException("夸克网盘账号Cookie无效请重新设置");
                    }
                } else {
                    throw new RuntimeException("夸克网盘账号Cookie无效请重新设置");
                }
            } catch (Exception e) {
                throw new RuntimeException("夸克网盘账号Cookie无效请重新设置");
            }
        }
        if (StrUtil.isEmpty(recastProperties.getRemotePathId())) {
            throw new RuntimeException("夸克网盘远程目录ID[RemotePathId]不能为空");
        } else {
            try (HttpResponse response = HttpRequest.get(FILE_LIST_URL + recastProperties.getRemotePathId()).header("Cookie", recastProperties.getCookie()).timeout(TIME_OUT * 1000).execute()) {
                if (response.isOk()) {
                    JSONArray jsonArray = JSONUtil.parseObj(response.body()).getJSONObject("data").getJSONArray("list");
                    if (CollUtil.isEmpty(jsonArray)) {
                        throw new RuntimeException("夸克网盘远程目录ID[RemotePathId]无效请重新设置");
                    }
                } else {
                    throw new RuntimeException("夸克网盘远程目录ID[RemotePathId]无效请重新设置");
                }
            } catch (Exception e) {
                throw new RuntimeException("夸克网盘远程目录ID[RemotePathId]无效请重新设置");
            }
        }
        if (StrUtil.isEmpty(recastProperties.getRemoteTmpPathId())) {
            throw new RuntimeException("夸克网盘远程临时目录ID[RemoteTmpPathId]不能为空");
        } else {
            try (HttpResponse response = HttpRequest.get(FILE_LIST_URL + recastProperties.getRemoteTmpPathId()).header("Cookie", recastProperties.getCookie()).timeout(TIME_OUT * 1000).execute()) {
                if (response.isOk()) {
                    JSONArray jsonArray = JSONUtil.parseObj(response.body()).getJSONObject("data").getJSONArray("list");
                    if (CollUtil.isNotEmpty(jsonArray)) {
                        throw new RuntimeException("夸克网盘远程临时目录ID[RemoteTmpPathId]目录下不能存在文件");
                    }
                } else {
                    throw new RuntimeException("夸克网盘远程临时目录ID[RemoteTmpPathId]无效请重新设置");
                }
            } catch (Exception e) {
                throw new RuntimeException("夸克网盘远程临时目录ID[RemoteTmpPathId]无效请重新设置");
            }
        }
    }

}
